#include <tsin/library/DynamicObject.hpp>
#include <tsin/library/DynamicBasicType.hpp>
#include <tsin/library/DynamicList.hpp>
#include <tsin/library/DynamicParameter.hpp>

#include <tsin/util/Logger.hpp>

#include <scdl/parser/Symbol.hpp>

#include <cstring>
#include <dlfcn.h>

using namespace tsin;
using namespace scdl;

class DummyObject : public DynamicObject
{
public:
        virtual void reinit()
        {
        }

        virtual char getDescriptor()
        {
                return ' ';
        }
} dummy;

void *DynamicObject::libHandle = 0;

bool DynamicObject::openLibrary(const char *path)
{
        libHandle = dlopen(path, RTLD_LAZY);
        return (libHandle != 0);
}

DynamicObject *DynamicObject::interpretInterface(Identifier *sym)
{
        Binder binderFunc = (Binder)dlsym(libHandle, sym->getBinder());
        if (!binderFunc) {
                return 0;
        }
        DynamicObject *ret = binderFunc();
        if (ret)
                ret->reinit();
        return ret;
}

DynamicObject *DynamicObject::interpretApplication(Application *sym)
{
        Symbol *applier = sym->getApplier();
        /* TODO what to do with the other types? */
        if (applier->getCategory() != CATEGORY_INTERFACE)
                return 0;

        DynamicObject *ret = interpretInterface(static_cast<Identifier *>(applier));
        for (int i = 0; i < sym->getNrArguments(); i ++) {
                DynamicObject *arg = DynamicObject::interpret(sym->getArgument(i));
                if (!arg)
                        return 0;
                ret->appendArgument(arg);
        }
        ret->reinit();
        return ret;
}

DynamicObject *DynamicObject::interpretList(List *sym)
{
        NamedSymbol *sort = static_cast<NamedSymbol *>(sym->getSort());
        DynamicObject *ret = DynamicList::getDynamicList(sort->getName());
        if (ret) {
                for (int i = 0; i < sym->getNrArguments(); i++) {
                        DynamicObject *arg = DynamicObject::interpret(sym->getArgument(i));
                        if (!arg)
                                continue;
                        ret->appendArgument(arg);
                }
                ret->reinit();
        }
        return ret;
}

DynamicObject *DynamicObject::interpret(Symbol *sym)
{
again:
        switch(sym->getCategory()) {
        case CATEGORY_BOOL:
                {
                        Boolean *b = static_cast<Boolean *>(sym);
                        DynamicBasicType *ret = new DynamicBasicType(b->getValue());
                        return ret;
                }
        case CATEGORY_NUMBER:
        {
                        Number *n = static_cast<Number *>(sym);
                        DynamicBasicType *ret = new DynamicBasicType(n->getValue());
                        return ret;
                }
        case CATEGORY_STRING:
                {
                        String *s = static_cast<String *>(sym);
                        DynamicBasicType *ret = new DynamicBasicType(s->getValue());
                        return ret;
                }
        case CATEGORY_INDUCTIVE_CTOR:
                {
                        Identifier *id = static_cast<Identifier *>(sym);
                        DynamicBasicType *ret = new DynamicBasicType(id->getInductiveCtorValue());
                        return ret;
                }
        case CATEGORY_PARAMETER:
                {
                        Parameter *param = static_cast<Parameter *>(sym);
                        DynamicParameter *ret = new DynamicParameter;
                        ret->init(param->getIndex());
                        return ret;
                }
        case CATEGORY_INTERFACE:
                {
                        Identifier *id = static_cast<Identifier *>(sym);
                        return interpretInterface(id);
                }
        case CATEGORY_APPLICATION:
                {
                        Application *app = static_cast<Application *>(sym);
                        return interpretApplication(app);
                }
        case CATEGORY_LIST:
                {
                        List *list = static_cast<List *>(sym);
                        return interpretList(list);
                }
        case CATEGORY_DEFINITION:
                sym = static_cast<Identifier *>(sym)->getDef();
                goto again;
        }

        return 0;
}

DynamicObject::DynamicObject(bool d)
        : nrArguments(0), dynamic(d)
{
        arguments = new DynamicObject*[8];
        desp = new char[9];
        for (int i = 0; i < 8; i++)
                arguments[i] = &dummy;
        memset(desp, 0, 9);
}

DynamicObject::~DynamicObject()
{
        for (int i = 0; i < nrArguments; i++)
                arguments[i]->unref();
        delete arguments;
        delete[] desp;
}

void DynamicObject::appendArgument(DynamicObject *arg)
{
        int i = nrArguments ++;
        arguments[i] = arg;
        desp[i] = arg->getDescriptor();
}

bool DynamicObject::isDynamic()
{
        return dynamic;
}

DynamicObject *DynamicObject::copy()
{
        this->ref();
        return this;
}

DynamicEvent DynamicObject::preTick(void *data, void *priv_data)
{
        DynamicEvent ret = EVENT_NONE;
        for (int i = 0; i < nrArguments; i++)
                ret |= arguments[i]->preTick(data, priv_data);
        if (ret & EVENT_REINIT)
                reinit();
        return ret;
}

DynamicEvent DynamicObject::postTick(void *data, void *priv_data)
{
        DynamicEvent ret = EVENT_NONE;
        for (int i = 0; i < nrArguments; i++)
                ret |= arguments[i]->postTick(data, priv_data);
        if (ret & EVENT_REINIT)
                reinit();
        return ret;
}

int DynamicObject::getInt()
{
        return 0;
}

float DynamicObject::getFloat()
{
        return 0.0f;
}

float DynamicObject::getFloatInRad()
{
        return getFloat() * 3.14159265f / 180.0f;
}

bool DynamicObject::getBool()
{
        return false;
}

const char *DynamicObject::getString()
{
        return "";
}

DynamicObject *DynamicObject::copy(DynamicObject *obj)
{
        obj->nrArguments = nrArguments;
        for (int i = 0; i < nrArguments; i++)
                obj->arguments[i] = arguments[i]->copy();
        memcpy(obj->desp, desp, 9);
        obj->reinit();
        return obj;
}

float DynamicObject::getData(void *data, int idx)
{
        if (!data || idx < 0)
                return 0.0f;
        float *fdata = static_cast<float *>(data);
        return fdata[idx];
}
